package org.example.executor.result;

import org.example.config.Configuration;
import org.example.executor.Executor;
import org.example.executor.parameter.ParameterHandler;
import org.example.mapping.BoundSql;
import org.example.mapping.MappedStatement;
import org.example.mapping.ResultMap;
import org.example.reflection.ObjectFactory;
import org.example.reflection.ReflectorFactory;
import org.example.session.DefaultResultHandler;
import org.example.session.ResultHandler;
import org.example.session.RowBounds;
import org.example.type.JdbcType;
import org.example.type.TypeHandlerRegistry;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;


public class DefaultResultSetHandler implements ResultSetHandler {

    private static final Object DEFERRED = new Object();

    private final Executor executor;
    private final Configuration configuration;
    private final MappedStatement mappedStatement;
    private final ParameterHandler parameterHandler;
    private final ResultHandler<?> resultHandler;
    private final BoundSql boundSql;
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final ObjectFactory objectFactory;
    private final ReflectorFactory reflectorFactory;
    private final RowBounds rowBounds;

    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
                                   RowBounds rowBounds) {
        this.executor = executor;
        this.configuration = mappedStatement.getConfiguration();
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.parameterHandler = parameterHandler;
        this.boundSql = boundSql;
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
        this.reflectorFactory = configuration.getReflectorFactory();
        this.resultHandler = resultHandler;
    }

    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        //结果处理器
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        //多个结果
        final List<Object> multipleResults = new ArrayList<>(defaultResultHandler.getResultList());
        //结果上下文对象
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        //拿到结果集
        ResultSetWrapper resultSetWrapper = new ResultSetWrapper( stmt.getResultSet(), configuration);
        //拿到结果集映射
        ResultMap resultMap = mappedStatement.getResultMaps().get(0);
        //处理行数据
        handleRowValuesForSimpleResultMap(resultSetWrapper,resultMap,defaultResultHandler,rowBounds,null);
        //添加结果
        multipleResults.add(defaultResultHandler.getResultList());

        return collapseSingleResultList(multipleResults);
    }

    private List<Object> collapseSingleResultList(List<Object> multipleResults) {
        return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
    }

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
            throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        ResultSet resultSet = rsw.getResultSet();
        while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
            Object rowValue = getRowValue(rsw, resultMap);
            callResultHandler(resultHandler, resultContext, rowValue);
        }
    }

    private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
        resultContext.setResultObject(rowValue);
        ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
    }


    private Object getRowValue(ResultSetWrapper rsw,ResultMap resultMap) {
        ResultSet resultSet = rsw.getResultSet();
        Class<?> resultType = resultMap.getResultType();
        try {
            //拿到对象
            Object obj = objectFactory.create(resultType);
            Field[] fields = obj.getClass().getDeclaredFields();
            List<JdbcType> jdbcTypes = rsw.getJdbcTypes();

            for (int i = 0 ; i < fields.length; i++) {
                //拿到字段
                Field field = fields[i];
                Object value = null;
                //拿到字段类型
                JdbcType jdbcType = jdbcTypes.get(i);
                switch (jdbcType){
                    case BIGINT : value = resultSet.getLong(field.getName()) ;break;
                    case VARCHAR : value = resultSet.getString(field.getName()) ;break;
                }

                if(null != value){
                    field.setAccessible(true);
                    field.set(obj,value);
                }
            }
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
        return context.getResultCount() < rowBounds.getLimit();
    }


    @Override
    public void handleOutputParameters(CallableStatement cs) throws SQLException {

    }
}